// Copyright  2004-2008 Backplane Ltd.
//
// This file is part of Ubiquity formsPlayer
//
// Ubiquity formsPlayer is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// Ubiquity formsPlayer is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License
// along with Ubiquity formsPlayer If not, see <http://www.gnu.org/licenses/>.
//	
//
//   Purpose: implementation of CFormControl.
//   $Id$


// FormControl.cpp: implementation of the CFormControl class.
//
//////////////////////////////////////////////////////////////////////

// FormControl.cpp: implementation of the CFormControl class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "XForms.h"
#include "XFormsApp.h"
#include "resource.h"
#include "FormControl.h"
#include "Model.h"
#include "DOM3Core.h"
#include "XFormsElement.h"
#include "XFormsXFormsElement.h"
#include "XFormsXPathFunctions.h"
#include "Actions.h"
#include "Vertex.h"
#include <fstream>
#include "namespaces.h"
#include "output.h"
#include "FormControlInputUI.h"
#include "RepeatUI.h"
#include "FP_Definitions.h"
#include "FP_ReturnCodes.h"
#include "ImplGroup.h"

#include "commonchild.h"
#include <Common/URI/msw/URISchemeUtils.h>
#include <Common/Internet/msw/InternetUtils.h>
#include "implInstance.h"
#include "implContent.h"
#include "ImplBind.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//maximum length of string that a long will convert to.
#define CHAR_LENGTH_OF_LONG 10

extern CXFormsApp theApp;

/*
 * [DONE]
 * Think of somewhere more acceptible to put this
 *	This isFunction is uncalled - 
 *	removed: bool IsFunction(BSTR sRef)
 *	version: 192
 */


//////////////////////////////////////////////////////////////////////
// CImplInstance Class
//////////////////////////////////////////////////////////////////////


const long g_lDefaultState = 3;//==g_lStateValid | g_lStateEnabled;

CFormControl::CFormControl()
:m_bInitializeDone(false)
	,m_bIncremental(false)
	,m_lState(g_lDefaultState)
	,m_bValueChanged(false)
	,m_bStateChanged(true)
	,m_bInitializeRunning(false)
	,m_bLateInitalise(false)

{
	m_pXFormsElement = NULL;
	m_bUIConstructed = false;
}

CFormControl::CFormControl(CXFormsElement* pElement)
	:CImpl((CXFormsElement*) pElement)
	,m_bInitializeDone(false)
	,m_bControlInRange(true)
	,m_bIncremental(false)
	,m_bLateInitalise(false)
	,m_lState(g_lDefaultState)
	,m_bValueChanged(false)
	,m_bStateChanged(true)
	,m_bInitializeRunning(false)
{
		 
	m_pXFormsElement = pElement;


	m_bUIConstructed = false;

	// Bug 459: Maintain list of form controls for debugging purposes.
	if(m_pXForms)
		m_pXForms->AddControl(this);
}

CFormControl::~CFormControl()
{

	//on deletion, remove ourselves from the model
	//	to prevent overdeletion.

	// Anything with a model is valid for
	//	deletion therefrom.
	if(m_pXForms && m_pModel)
	{
		m_pModel->RemoveFormControl(this);
	}
	//only delete from form level when there is no model
	// m_pXForms only keeps track of orphans.
	else if(m_pXForms)
	{
		m_pXForms->RemoveFormControl(this);
	}
	if(m_pXForms)
	{
		m_pXForms->m_UndisplaidOrphans.RemoveKey(this);
	}
}


HRESULT CFormControl::init(CImpl * pParent)
{
	CImpl::init(pParent);
	if(!m_bisBound)	 
	{
		//Things like Switches and buttons are unbound, 
		// don't bother finding a model for them to attach to.
		//Same goes for captions with inline data
		//don't add to the regular orphan list, this is done in setModel(0)
		//m_pXForms->m_Orphans.AddTail(this);
		setModel(0);
		// Bug 836 - Change m_UndisplaidOrphans & m_Orphans to improve performance
		m_pXForms->m_UndisplaidOrphans.SetAt(this, this);
	}
	return S_OK;
}

bool CFormControl::IsAppropriateToInitializeNow()
{
	return true;

}

HRESULT CFormControl::doInitialize()
{
	HRESULT hr = S_OK;
	
	hr = formControlInitialize();

	if(SUCCEEDED(hr))
		hr = refresh();
	
	return hr;
}

HRESULT CFormControl::refresh()
{
#if DEBUG_LEVEL & DEBUG_MONITOR_EVENTS
	AtlTrace("'formsplayer.dll': called CFormControl::refresh on %X, %S\n",this,m_sId);
#endif
	HRESULT hr = S_OK;
	FireStateEvents();

	return hr;
}

HRESULT CFormControl::onDOMFocusIn()
{
	HRESULT hr = S_FALSE;
	if(receiveFocus())
	{
		hr = FireEvent(EVENT_DOMFOCUS_IN);
	}
	return hr;
}

HRESULT CFormControl::onDOMFocusOut()
{
	HRESULT hr = S_OK;
	hr = FireEvent(EVENT_DOMFOCUS_OUT);
	return hr;

}

HRESULT CFormControl::onfocus()
{
	//This is the Interaction event xforms-focus,
	// Do not confuse it with DOMFocusIn,  It is 
	// Dispatched in response to: set focus to a form control
	// and has a default action of setting the focus.
	// DOMFocusIn is just a notification event.
	HRESULT hr = S_FALSE;
	if(receiveFocus())
	{
		hr = FireEvent(EVENT_FOCUS);
		if(SUCCEEDED(hr))
			hr = showFocus();
	}
	return hr;
}

HRESULT CFormControl::onactivate()
{
	HRESULT hr = S_OK;
	hr = FireEvent(EVENT_ACTIVATE);
	return hr;
}

HRESULT CFormControl::onhelp()
{
	return FireEvent(EVENT_HELP);
}

HRESULT CFormControl::onhint()
{
	return FireEvent(EVENT_HINT);
}

HRESULT CFormControl::onnext()
{
	return FireEvent(EVENT_NEXT);
}

HRESULT CFormControl::onprevious()
{
	return FireEvent(EVENT_PREVIOUS);
}

HRESULT CFormControl::onvaluechanging()
{
	return FireEvent(EVENT_VALUECHANGING);
}

HRESULT CFormControl::ondeselect() 
{
	if(m_pXFormsElement)
		m_pXFormsElement->DeselectedEffect();
	return FireEvent(EVENT_DESELECT);
}

HRESULT CFormControl::onselect()
{
	if(m_pXFormsElement)
		m_pXFormsElement->SelectedEffect();
	return FireEvent(EVENT_SELECT);
}


HRESULT CFormControl::putValue(BSTR const /*sVal*/)
{
	return S_OK;
}

void CFormControl::onBindToNothing()
{
	if(m_pXFormsElement)
	{
		//Bound to nothing should pretend to be disabled.
		ondisabled();
	}
}

HRESULT CFormControl::formControlInitialize()
{	
	//The processing outlined in 
	//	http://www.w3.org/TR/2002/CR-xforms-20021112/slice4.html#evt-formControlInitialize
	//	is performed in CImplNodeBinding::formControlInitialize();
	//The inheritance model is not quite correct.
	FireEvent(EVENT_FORMCONTROLINITIALIZE);
	//Just gives the front end a chance to do any first-time processing.
	if(m_pXFormsElement)
		return m_pXFormsElement->formControlInitialize();

	return S_OK;
}

HRESULT CFormControl::setFocus()
{
	HRESULT hr = S_FALSE;
	if(receiveFocus())
	{
		hr = FireEvent(EVENT_FOCUS);
		if(SUCCEEDED(hr))
			hr = showFocus();
		
		//insulate the outside from failing HRESULTs
		if(FAILED(hr))
			hr = S_FALSE;
	}
	else if(!m_pXForms->isDocumentComplete())
	{
		m_pXForms->AddControlAwaitingFocus(this);
	}


	return hr;
}

bool CFormControl::receiveFocus()
{
	return true;
}

HRESULT CFormControl::showFocus()
{
	if(m_pXFormsElement)
		return m_pXFormsElement->focus();
	else
		return S_FALSE;
}



HRESULT CFormControl::blank()
{
	//If I already know that there is no data
	//	to put into the control, put NULL into it.
	//	no need to look it up.

	if(m_pXFormsElement)
	{
		m_pXFormsElement->put_value(NULL);
	}
	

	return S_OK;
}

/*
HRESULT CImpl::AddChild(CImpl *pChild)
{
	//default behaviour is to do nothing.
	return S_OK;
}

HRESULT CImpl::RemoveChild(CImpl* pImpl)
{
	return S_OK;
}
*/

HRESULT CImplCaption::doInitialize()
{
/*	if(!((CFormControl *)m_pParent)->m_bInitializeDone)
	{
		// cannot give a caption to an uninitialized control
		//	if it is in that state, initialise it.
		((CFormControl *)m_pParent)->initialize();
	}
*/	return CImplCommonChild::doInitialize();

}



void CFormControl::setModel(CImplModel* pModel)
{
	if(m_pModel != pModel || !m_bInitializeDone)
	{
		//Remove from containing list (if any)
		if(m_pModel)
			m_pModel->RemoveFormControl(this);
		else if(m_pXForms)
			m_pXForms->RemoveFormControl(this);

		//Set the new model pointer
		const_cast<CImplModel*>(m_pModel) = pModel;
		
		//Add to new containing list.
		if(m_pModel)
			m_pModel->AddFormControl(this);
		else if(m_pXForms)
		{
			// Bug 836 - Change m_UndisplaidOrphans & m_Orphans to improve performance
			CFormControl * pFC = NULL;
			// If the form control isn't already in the map then add it.
			if(!m_pXForms->m_Orphans.Lookup(this, pFC))
				m_pXForms->m_Orphans.SetAt(this, this);
		}
	}
}

void CFormControl::notifyDelete()
{
	if(m_pXFormsElement)
	{
		m_pXFormsElement->formControlUnInitialize();
	}


	if(m_pModel)
	{
		m_pModel->deleteControl(this);
	}
	else if(m_pXForms)
	{
		m_pXForms->deleteControl(this);
	}
	else
	{
		//I don't know?
	}
}


// called by the front-end control 
//	when a given value can be displayed.
HRESULT CFormControl::onInRange(void)
{
	HRESULT hr = S_FALSE;
	if(!m_bControlInRange)
	{
		m_bControlInRange = true;
		hr = RunCustomBehaviour(EVENT_INRANGE);
	}
	return hr;
}
// called by the front-end control 
//	when a given value can't be displayed.
HRESULT CFormControl::onOutOfRange(void)
{
	HRESULT hr = S_FALSE;
	if(m_bControlInRange)
	{
		m_bControlInRange = false;
		hr = RunCustomBehaviour(EVENT_OUTOFRANGE);
	}
	return hr;
}

void CFormControl::SetControlType(const wchar_t * const lpwszControlType)
{
	m_sControlType.Empty();
	m_sControlType = (lpwszControlType ? lpwszControlType : L"<unknown element>");
}

